﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Linq;
using Cubicle.Core.Interfaces;
using Cubicle.Core.Processors;
using Cubicle.Core.Shared;

namespace Cubicle.Core.Scripting
{

    [Export(typeof(IScriptManager))]
    [ExportMetadata("Name", "ScriptManager")]
    public class ScriptManager : IScriptManager
    {
        // Nlog
        private static NLog.Logger _logger = NLog.LogManager.GetCurrentClassLogger();
        private const string AutorunDir = "autorun";

        private static string GetAutorunFolder()
        {
            var baseDir = AppDomain.CurrentDomain.BaseDirectory;
            var autorunDir = Path.GetFullPath(Path.Combine(baseDir, AutorunDir));
            if (Directory.Exists(autorunDir)) return autorunDir;
            autorunDir = Path.GetFullPath(Path.Combine(baseDir, @"..\" + AutorunDir));
            if (Directory.Exists(autorunDir)) return autorunDir;
            return null;
        }

        private IRepository _repo;
        private IInvoker _processor;
        private readonly FileSystemWatcher _autorunWatcher;
        private readonly string _autorunDir;

        [ImportingConstructor]
        public ScriptManager(IRepository repo, IInvoker processor)
        {
            _repo = repo;
            _processor = processor;

            // Set up autorun watcher
            _autorunDir = GetAutorunFolder();
            if (_autorunDir == null) return;
            _autorunWatcher = new FileSystemWatcher
            {
                Filter = "*.cube", 
                Path = _autorunDir,
                EnableRaisingEvents = true
            };
            _autorunWatcher.Created += AutorunCreated;

            // Run scripts in autorun directory
            var directory = new DirectoryInfo(_autorunDir);
            foreach (var item in directory.EnumerateFiles("*.cube").OrderBy(x => x.Name).ToList())
            {
                var script = Helpers.ReadBytesFromFile(item.FullName);
                RunByteScript(script);
            }
        }

        private void AutorunCreated(object sender, FileSystemEventArgs e)
        {
            var script = Helpers.ReadBytesFromFile(e.FullPath);
            RunByteScript(script);
        }

        public bool RunByteScript(byte[] rawScript)
        {
            try
            {
                // Convert string to XML
                _logger.Info("Decoding script...");
                var script = Helpers.BinaryToXml<CubicleScript>(rawScript);

                return script != null && RunScript(script);
            }
            catch (Exception e)
            {
                _logger.ErrorException(e, "Error executing script");
                return false;
            }
        }

        public bool RunScript(CubicleScript script)
        {
            try
            {
                // Process all instructions
                Dictionary<string, string> dict = null;
                if (script.Dict != null)
                    dict = script.Dict.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
                script.Instructions.Iter(i => Process(i, dict));

                return true;
            }
            catch (Exception e)
            {
                _logger.ErrorException(e, "Script execution error.");
                return false;
            }
        }

        private void Process(Instruction inst, Dictionary<string, string> dict)
        {
            try
            {
                inst.Parameters = inst.GetParameters(_repo, dict);
                _processor.Process(inst);
            }
            catch (Exception e)
            {
                var message = "Script execution error." + Helpers.XmlToString(inst);
                _logger.ErrorException(e, message);
            }
        }

    }
}
